package com.concurrent.sample.common.util;

import static java.math.BigDecimal.ONE;
import static java.math.BigDecimal.ZERO;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.function.Function;

/**
 * BigDecimal 辅助类
 */
public class BigDecimalUtil {

    private BigDecimalUtil() {}

    /**
     * 100
     **/
    public static final BigDecimal HUNDRED = new BigDecimal("100");

    /**
     * 100
     **/
    public static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_UP;

    /**
     * 若 num 为 null 则返回 0，否则返回 num 本身
     */
    public static BigDecimal nullToZero(BigDecimal num) {
        if (num == null) {
            return ZERO;
        }
        return num;
    }

    /**
     * 求和，null 认为是 0
     */
    public static BigDecimal add(BigDecimal... nums) {
        return Arrays.stream(nums)
                .map(BigDecimalUtil::nullToZero)
                .reduce(ZERO, BigDecimal::add);
    }

    /**
     * 求和，null 认为是 0
     */
    public static BigDecimal add(List<BigDecimal> items) {
        return add(items, Function.identity());
    }

    /**
     * 求和，null 认为是 0
     *
     * @param items     求和项
     * @param extractor 取求和项
     * @return 求和结果
     */
    public static <T> BigDecimal add(List<T> items, Function<T, BigDecimal> extractor) {
        if (CollUtil.isEmpty(items)) {
            return ZERO;
        }
        return items.stream().filter(Objects::nonNull).map(extractor).reduce(ZERO, BigDecimal::add);
    }

    /**
     * 做减法，null 认为是 0
     */
    public static BigDecimal minus(BigDecimal num1, BigDecimal num2) {
        return nullToZero(num1).subtract(nullToZero(num2));
    }

    /**
     * 求积，null 认为是 0，自定义计算结果精度
     *
     * @param scale        保留位数精度
     * @param roundingMode 舍入模式
     * @param nums         参与求积项
     * @return num1 * num2 * ..... * numN
     */
    public static BigDecimal multiply(int scale, RoundingMode roundingMode, BigDecimal... nums) {
        return multiply(nums).setScale(scale, roundingMode);
    }

    /**
     * 求积，null 认为是 0
     */
    public static BigDecimal multiply(BigDecimal... nums) {
        return Arrays.stream(nums).map(BigDecimalUtil::nullToZero)
                .reduce(ONE, BigDecimal::multiply);
    }

    /**
     * 取整，null 认为是 0
     */
    public static BigDecimal divideToIntegralValue(BigDecimal num1, BigDecimal num2) {
        if (isZero(num2)) {
            return ZERO;
        }
        return nullToZero(num1).divideToIntegralValue(nullToZero(num2));
    }

    /**
     * 取余，null 认为是 0
     */
    public static BigDecimal remainder(BigDecimal num1, BigDecimal num2) {
        if (isZero(num2)) {
            return ZERO;
        }
        return nullToZero(num1).remainder(nullToZero(num2));
    }

    /**
     * 做除法，null 认为是 0
     */
    public static BigDecimal divide(BigDecimal num1, BigDecimal num2) {
        return divide(num1, num2, 20, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 做除法，null 认为是 0
     */
    public static BigDecimal divide(BigDecimal num1, BigDecimal num2, int scale, RoundingMode roundingMode) {
        if (isZero(num2)) {
            return ZERO;
        }
        return nullToZero(num1).divide(nullToZero(num2), scale, roundingMode);
    }

    /**
     * 是否为 0，null 认为是 0
     */
    public static boolean isZero(BigDecimal num) {
        return ZERO.compareTo(nullToZero(num)) == 0;
    }

    /**
     * 是否不为 0，null 认为是 0
     */
    public static boolean isNotZero(BigDecimal num) {
        return !isZero(num);
    }

    /**
     * 取最大值，null 被跳过，若全为 null 则返回 null
     */
    public static BigDecimal max(BigDecimal... nums) {
        return Arrays.stream(nums).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(null);
    }

    /**
     * 取最小值，null 被跳过，若全为 null 则返回 null
     */
    public static BigDecimal min(BigDecimal... nums) {
        return Arrays.stream(nums).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(null);
    }

    /**
     * 取平均值
     * <p>
     *     nums 为 null 或空数组时，返回 null；
     *     否则返回 nums 的平均值，nums 中存在 null 项时视为 0 处理
     * </p>
     */
    public static BigDecimal average(BigDecimal... nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        return divide(add(nums), BigDecimal.valueOf(nums.length));
    }

    /**
     * 舍位，rounding mode 使用 DEFAULT_ROUNDING_MODE，即 HALF_UP
     */
    public static BigDecimal round(BigDecimal num, int scale) {
        if (num == null) {
            return null;
        }
        return num.setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 使用java正则表达式去掉多余的0
     *
     * @param s
     * @return
     */
    public static BigDecimal subZeroAndDot(BigDecimal s) {
        String str = s.toString();
        if (str.indexOf(".") > 0) {
            str = str.replaceAll("0+?$", "");//去掉多余的0
        }
        return new BigDecimal(str);
    }

    /**
     * 小于
     */
    public static boolean lt(BigDecimal val1, BigDecimal val2) {
        return val1.compareTo(val2) < 0;
    }

    // 等于
    public static boolean eq(BigDecimal val1, BigDecimal val2) {
        return val1.compareTo(val2) == 0;
    }

    public static boolean eqWithNull(BigDecimal a, BigDecimal b) {
        // 都为 null
        if (null == a && null == b) {
            return true;
        }
        // 有一个为 null
        if (null == a || null == b) {
            return false;
        }

        return a.compareTo(b) == 0;
    }

    // 全部等于
    public static boolean eqAll(BigDecimal... nums) {
        BigDecimal num0 = nums[0];
        for (BigDecimal num : nums) {
            if (neq(num0, num)) {
                return false;
            }
        }
        return true;
    }

    // 全部等于(忽略null值)
    public static boolean eqAllIgnoreNull(BigDecimal... nums) {
        BigDecimal num0 = nums[0];
        for (BigDecimal num : nums) {
            if (num == null) {
                continue;
            }
            if (num0 == null) {
                num0 = num;
            }
            if (neq(num0, num)) {
                return false;
            }
        }
        return true;
    }

    // 不等于
    public static boolean neq(BigDecimal val1, BigDecimal val2) {
        return val1.compareTo(val2) != 0;
    }

    // 大于
    public static boolean gt(BigDecimal val1, BigDecimal val2) {
        return val1.compareTo(val2) > 0;
    }

    // 大于等于
    public static boolean ge(BigDecimal val1, BigDecimal val2) {
        return val1.compareTo(val2) > -1;
    }

    // 小于等于
    public static boolean le(BigDecimal val1, BigDecimal val2) {
        return val1.compareTo(val2) < 1;
    }

    /**
     * 设精度，null认为是0
     */
    public static BigDecimal setScaleNullToZero(BigDecimal value, Integer precision) {
        if (value == null || precision == null) {
            return nullToZero(value);
        }
        return value.setScale(precision, DEFAULT_ROUNDING_MODE);
    }


    /**
     * null为0
     *
     * @param value        数值
     * @param precision    保留的小数位
     * @param roundingMode 舍入模型
     * @return 舍入后的值
     */
    public static BigDecimal setScaleNullToZero(BigDecimal value, Integer precision, RoundingMode roundingMode) {
        if (value == null || precision == null) {
            return nullToZero(value);
        }
        // 为空,使用默认 舍入模型
        roundingMode = ObjectUtil.isNull(roundingMode) ? DEFAULT_ROUNDING_MODE : roundingMode;
        return value.setScale(precision, roundingMode);
    }


    /**
     * 设精度，null直接跳过
     */
    public static BigDecimal setScaleIgnoreNull(BigDecimal value, Integer precision) {
        if (value == null || precision == null) {
            return value;
        }
        return value.setScale(precision, DEFAULT_ROUNDING_MODE);
    }


    /**
     * null则返回
     *
     * @param value        数值
     * @param precision    保留的小数位
     * @param roundingMode 舍入模型
     * @return 舍入后的值
     */
    public static BigDecimal setScaleIgnoreNull(BigDecimal value, Integer precision, RoundingMode roundingMode) {
        if (value == null || precision == null) {
            return value;
        }
        // 为空,使用默认 舍入模型
        roundingMode = ObjectUtil.isNull(roundingMode) ? DEFAULT_ROUNDING_MODE : roundingMode;
        return value.setScale(precision, roundingMode);
    }

    /**
     * 转换字符串为BigDecimal
     *
     * @param source s
     * @return bigDecimal对象
     */
    public static BigDecimal transferStrToBigDecimal(String source) {
        if (CharSequenceUtil.isEmpty(source)) {
            return null;
        }

        //处理字符格式中逗号写法
        StringTokenizer st = new StringTokenizer(source, ",");
        StringBuilder sb = new StringBuilder();
        while (st.hasMoreTokens()) {
            sb.append(st.nextToken());
        }

        source = sb.toString();

        // 去除末尾0
        return new BigDecimal(source).stripTrailingZeros();
    }

    public static String numberPrint(BigDecimal number) {
        return Objects.isNull(number) ? "null" : number.toPlainString();
    }
}
